package com.study.stream;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import org.junit.Test;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class StreamTest {

	@Test
	// parallelStream 返回一个可以并行的流
	public void parallelStreamTest() {
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
		numbers.parallelStream()
				// .forEachOrdered(a -> System.out.print(a)); //123456789 顺序输出
				// .forEach(a -> System.out.print(a)); //893645721
				// .forEach(System.out::print); //653421789
				.forEach(new PrintUtil()::print); // 682319574 等同于：
													// System.out::print
	}

	@Test
	// stream 返回一个有顺序的流
	public void streamTest() {
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
		numbers.stream()
				// .forEachOrdered(a -> System.out.print(a)); //123456789 顺序输出
				// .forEach(a -> System.out.print(a)); //123456789 顺序输出
				.forEach(System.out::print); // 123456789 顺序输出
		// .forEach(new PrintUtil()::print); //123456789 等同于： System.out::print

	}

	@Test
	// stream 返回一个有顺序的流
	public void streamTest1() {
		String[] arr = new String[] { "a", "b", "c", "d", "e", "f", "g", "h" };
		Arrays.stream(arr, 0, 1).forEach(System.out::print);
		// Arrays.stream(arr,startInclusive,endExclusive) 下标从零开始,
		// 不包含endExclusive
	}

	@Test
	public void StreamBuilderTest() {
		Stream<String> streamBuilder = Stream.<String>builder().add("a")
				.add("b").add("c").build();
		streamBuilder.forEach(a -> System.out.println(a));

		// generate会创建一个无限大的Stream直到内存上限，用limit限制大小
		Stream<String> streamGenerated = Stream.generate(() -> "element")
				.limit(3);
		streamGenerated.forEach(a -> System.out.println(a));

		// 从40作为第一个元素开始，每个元素递增2，一共20个元素
		Stream<Integer> streamIterated = Stream.iterate(40, n -> n + 2)
				.limit(5);
		streamIterated.forEach(a -> System.out.println(a));
	}

	@Test
	public void StreamTest2() {
		IntStream intStream = IntStream.range(1, 3);
		intStream.forEach(a -> System.out.println(a)); // 12
		LongStream longStream = LongStream.rangeClosed(1, 3); // 123
		longStream.forEach(a -> System.out.print(a));

		System.out.println("\nrandom.int");
		Random random = new Random();
		IntStream stream = random.ints(2); // 获取两个整形数
		stream.forEach(a -> System.out.println(a));
	}

	@Test
	public void FileStreamTest() {
		Path path = Paths.get("c:\\tixml.log");
		try {
			Stream<String> lines = Files.lines(path, Charset.forName("UTF-8"));
			lines.forEach(System.out::println);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Test
	public void StreamTest3() {
		//一般Stream的使用如前文所述，分为三步：源处理，中间处理，结束处理
		Stream<String> stream = Stream.of("a", "b", "c", "d").skip(1);
		long count = stream.skip(1).map(element -> element + element).count();
		System.out.println("count: " + count);
	}

	@Test
	public void StreamTest4() {
		//Stream不可以复用（经过结束处理的）
		Stream<String> stream = Stream.of("a", "b", "bbb", "bb", "c")
				.filter(element -> element.contains("b"));
		//stream.forEach(a -> System.out.print(a));  //下面语句会报错，Stream不可以复用
		Optional<String> anyElement = stream.findAny();
		// 如果存在，输出该元素，不存在则输出--
		System.out.println(anyElement.orElse("--")); //b

		List<String> elements = Stream.of("a", "b", "bbb", "bb", "c")
				.filter(element -> element.contains("b"))
				.collect(Collectors.toList());
		anyElement = elements.stream().findAny();
		System.out.println(anyElement.orElse("--")); //b
		Optional<String> firstElement = elements.stream().findFirst();
		System.out.println(firstElement.orElse("--")); //b
	}
	@Test
	public void StreamTest5(){
		//Stream懒调用  工作流中的中间调用是懒调用，意思就是如果没有结束处理，那么中间处理是不会被执行的。
		List<String> list = Arrays.asList("abc1", "abc2", "abc3");
		list.stream().filter(element -> {
		    System.out.println("中间处理" + element);
		    return element.contains("2");
		});
		System.out.println("“中间处理”是不会输出的 ");  
		
		List<String> list1 = Arrays.asList("abc1", "abc2", "abc3");
		list1.stream().filter(element -> {
		    System.out.println("中间处理" + element);
		    return element.contains("2");
		}).findAny();
		System.out.println("“中间处理”是会输出的 ");
		
	}
	@Test
	public void StreamTestReduce6(){
		//reduce 的用法
		int reducedParams = Stream.of(1, 2, 3)
		.reduce(10, (a, b) -> a + b, (a, b) -> {
		 log.info("combiner was called");
		 return a + b;
		});
		System.out.println(reducedParams);  //16
		
		/**
		 reduce()包括三个参数：
identity：accumulator的初始值，并且是在Stream为空时的默认值
accumulator：合并逻辑，在每一步合并两个元素，只有最后一步的有用，但是用这个效率不高
combiner：accumulator的并发改进版，但必须在ParallelStream的上下文下运行，同时在有identity初始值的情况下结果与accumulator不一样
		 */
		int reducedParallel = Arrays.asList(1, 2, 3).parallelStream()
	    .reduce(10, (a, b) -> a + b, (a, b) -> {
	       log.info("combiner was called");
	       return a + b;
	    });
		System.out.println(reducedParallel);  //36
	}
	@Test
	public void StreamTestCollect(){
		List<PrintUtil> list = Arrays.asList(new PrintUtil("aa", 12), new PrintUtil("bb", 13),new PrintUtil("cc", 14));
		
		//将Stream转换为List或者Set
		List<String> collectorCollection = 
				list.stream().map(PrintUtil::getName).collect(Collectors.toList());
		System.out.println(collectorCollection.toString());  //[aa, bb, cc]
		
		String collect = list.stream().map(PrintUtil::getName).collect(Collectors.joining(",", "[>", "<]"));
		System.out.println(collect); //[>aa,bb,cc<]
		
		//求对象某一数值域平均值
		int avgAge = list.stream().collect(Collectors.averagingInt(PrintUtil::getAge)).intValue();
		System.out.println("平均年龄: " + avgAge);
		
		//求对象某一数值域总和
		System.out.println(list.stream().collect(Collectors.summingInt(PrintUtil::getAge)));
		
		//求对象某一数值域统计数据
		System.out.println(list.stream().collect(Collectors.summarizingInt(PrintUtil::getAge)));
		
		//按照某一对象域分类
		System.out.println(list.stream().collect(Collectors.groupingBy(PrintUtil::getName)));
		
	}
	
	@Test
	public void ParallelStreamtest(){
		//ParallelStream的作用, 具有平行处理能力，处理的过程会分而治之，也就是将一个大任务切分成多个小任务，这表示每个任务都是一个操作
		//parallelStream背后的男人:ForkJoinPool
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
		numbers.parallelStream()
		       .forEach(System.out::println);  
		//工作窃取（work-stealing）算法就是整个forkjion框架的核心理念,工作窃取（work-stealing）算法是指某个线程从其他队列里窃取任务来执行。
		//工作窃取算法的优点是充分利用线程进行并行计算，并减少了线程间的竞争，其缺点是在某些情况下还是存在竞争，比如双端队列里只有一个任务时。并且消耗了更多的系统资源，比如创建多个线程和多个双端队列。
	}
}

@Slf4j
@Data
// 写一个Consumer<T>接口的实现类, 等同于： System.out::print
class PrintUtil {
	
	private String name;
	private int age;
	
	public void print(Object a) {
		System.out.print(a + "");
	}
	
	public PrintUtil(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public PrintUtil() {
		// TODO Auto-generated constructor stub
	}
}
